#include "PD3DCamera.h"

PD3DCamera::PD3DCamera(LPDIRECT3DDEVICE9 device)
{
	mPosition = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	mLookAt = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
	mRight = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
	mUp = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
	mUpdateRequired = false;
	mRotAroundLookAt = mRotAroundRight = mRotAroundUp = 0.0f;
	D3DXMatrixIdentity(&mViewTransform);
	mDevice = device;
}

VOID PD3DCamera::LookAtPos(D3DXVECTOR3 *position, D3DXVECTOR3 *lookAt, D3DXVECTOR3 *up)
{
	D3DXMatrixLookAtLH(&mViewTransform, position, lookAt, up);

	mPosition = *position;

	mRight.x = mViewTransform._11;
	mRight.y = mViewTransform._21;
	mRight.z = mViewTransform._31;

	mUp.x = mViewTransform._12;
	mUp.y = mViewTransform._22;
	mUp.z = mViewTransform._32;

	mLookAt.x = mViewTransform._13;
	mLookAt.y = mViewTransform._23;
	mLookAt.z = mViewTransform._33;

	mRotAroundLookAt = mRotAroundRight = mRotAroundUp = 0.0f;
}

VOID PD3DCamera::SetPosition(FLOAT x, FLOAT y, FLOAT z)
{
	mPosition = D3DXVECTOR3(x, y, z);
	mUpdateRequired = true;
}
	
VOID PD3DCamera::Update()
{
	if (mDevice == NULL)
	{
		throw new string("PD3DCamera::Update() - Device cannot be NULL.");
	}

	if (mUpdateRequired)
	{
		UpdateCameraMatrices();
	}
	else
	{
		mDevice->SetTransform(D3DTS_VIEW, &mViewTransform);
	}
}

VOID PD3DCamera::UpdateCameraMatrices()
{
	D3DXMATRIX matTotal, matRotAroundUp, matRotAroundRight, matRotAroundLookAt;
	D3DXMatrixRotationAxis(&matRotAroundRight, &mRight, mRotAroundRight);
	D3DXMatrixRotationAxis(&matRotAroundUp, &mUp, mRotAroundUp);
	D3DXMatrixRotationAxis(&matRotAroundLookAt, &mLookAt, mRotAroundLookAt);

	D3DXMatrixMultiply(&matTotal, &matRotAroundUp, &matRotAroundRight);
	D3DXMatrixMultiply(&matTotal, &matRotAroundLookAt, &matTotal);

	D3DXVec3TransformCoord(&mRight, &mRight, &matTotal);
	D3DXVec3TransformCoord(&mUp, &mUp, &matTotal);
	D3DXVec3Cross(&mLookAt, &mRight, &mUp);

	if (fabs(D3DXVec3Dot(&mUp, &mRight)) > 0.01f)
	{
		D3DXVec3Cross(&mUp, &mLookAt, &mRight);
	}

	D3DXVec3Normalize(&mRight, &mRight);
	D3DXVec3Normalize(&mUp, &mUp);
	D3DXVec3Normalize(&mLookAt, &mLookAt);

	float view41, view42, view43;
	view41 = -D3DXVec3Dot(&mRight, &mPosition);
	view42 = -D3DXVec3Dot(&mUp, &mPosition);
	view43 = -D3DXVec3Dot(&mLookAt, &mPosition);

	mViewTransform = D3DXMATRIX(mRight.x, mUp.x, mLookAt.x, 0.0f,
								mRight.y, mUp.y, mLookAt.y, 0.0f,
								mRight.z, mUp.z, mLookAt.z, 0.0f,
								view41, view42, view43, 1.0f);

	mRotAroundLookAt = mRotAroundRight = mRotAroundUp = 0.0f;
	mUpdateRequired = false;

	if (FAILED(mDevice->SetTransform(D3DTS_VIEW, &mViewTransform)))
		throw new string("UpdateCameraMatrices::SetTransform()");
}

D3DXVECTOR3* PD3DCamera::GetPosition() { return &mPosition; }

D3DXVECTOR3* PD3DCamera::GetLookAt() { return &mLookAt; }

D3DXVECTOR3* PD3DCamera::GetRight() { return &mRight; }

D3DXVECTOR3* PD3DCamera::GetUp() { return &mUp; }

D3DXMATRIX* PD3DCamera::GetViewMatrix() { return &mViewTransform; } 

VOID PD3DCamera::RotateDown(FLOAT angle)
{
	mRotAroundRight += angle;
	mUpdateRequired = true;
}

VOID PD3DCamera::RotateRight(FLOAT angle)
{
	mRotAroundUp += angle;
	mUpdateRequired = true;
}

VOID PD3DCamera::Roll(FLOAT angle)
{
	mRotAroundLookAt += angle;
	mUpdateRequired = true;
}

VOID PD3DCamera::MoveFoward(FLOAT dist)
{
	mPosition += dist * mLookAt;
	mUpdateRequired = true;
}

VOID PD3DCamera::MoveRight(FLOAT dist)
{
	mPosition += dist * mRight;
	mUpdateRequired = true;
}

VOID PD3DCamera::MoveUp(FLOAT dist)
{
	mPosition += dist * mUp;
	mUpdateRequired = true;
}

VOID PD3DCamera::MoveInDirection(FLOAT dist, D3DXVECTOR3* dir)
{
	D3DXVECTOR3 aux(0.0f, 0.0f, 0.0f);
	D3DXVec3Normalize(&aux, dir);
	mPosition += dist * aux;
	mUpdateRequired = true;
}